//== SAGITTARIUS ============================================================================

//== FILEDOC =========================================================================

/** @file scanner.h
  * @brief The scanner
  * 
  * @page GhostTokens "Ghost Tokens"
  * Peter Landin introduced two new symbols in order to make it possible to
  * parse his ISWIM language using a phrase-level grammar (see Landin '66).
  * In this implementation, these symbols do not actually appear on the input, 
  * but are delivered to the parser.  They are referred to as @a ghost @a tokens.
  * These pairs of tokens are maintained by a stack that keeps track of the level
  * of indentation.  Increasing the level causes @ref TOKEN_bopen to be emitted,
  * whereas decreasing it causes the emission of @ref TOKEN_bclose.  Tabulator characters
  * are not yet supported.
  */

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGUTTARUIS__SCANNER__H_
#define __SAGUTTARUIS__SCANNER__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/parser/pAST.h"
#include "sagittarius/lexer/lexcore.h"

/// @ref scannerNextTok Returns this value to indicate an error
#define SCANNER_INVALID_TOK 0xfffffff0
/// @ref scannerNextTok Returns this value to indicate end of input
#define SCANNER_EOI 0xffffff00

// Tokens from lexer
#define TOKEN_ws 0
#define TOKEN_nl 1
#define TOKEN_id 2
#define TOKEN_lparen 3
#define TOKEN_rparen 4
#define TOKEN_lbrack 5
#define TOKEN_rbrack 6
#define TOKEN_lbrace 7
#define TOKEN_rbrace 8
#define TOKEN_eq 9
#define TOKEN_plus 10
#define TOKEN_dash 11
#define TOKEN_star 12
#define TOKEN_slash 13
#define TOKEN_amp 14
#define TOKEN_comma 15
#define TOKEN_dot 16
#define TOKEN_carrot 17
#define TOKEN_tilde 18
#define TOKEN_semicol 19
#define TOKEN_dollar 20
#define TOKEN_arrow 21
#define TOKEN_strsng 22
#define TOKEN_strdbl 23
#define TOKEN_integral 24
#define TOKEN_nintegral 25

// User-defined tokens
/// Begin block
#define TOKEN_bopen  0xfff0
/// End block
#define TOKEN_bclose 0xfff1

/// Symbol used to explicitly open a block
#define OPEN_BLOCK_SYMBOL TOKEN_lbrace
#define CLOSE_BLOCK_SYMBOL TOKEN_rbrace

typedef struct {
    constLexBuf w; /* Position in the input buf */
    constLexBuf strt; /* Starting position of accepted pattern (where ac = true) */
    uint32 s; /* State */
    uint32 ef; /* Error flag */
    uint32 ac; /* Accepted pattern */
    uint32 indent; /* User variable */
} sm_c_parserState;

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    /// Numeric value of token
    uint32 tok;
    /// Starting position of the token
    constLexBuf pos;
    /// Length of the string
    size_t l;
    /// Line number of the token
    uint64 ln;
} sg_token_base;

/// Deprecated
#define TOKEN_EQ(a, b) ((a).tok == (b).tok)

/// @brief Type used for token
typedef sg_token_base sg_token_t;

typedef struct __token_stack_link {
    /// Capacity
    size_t c;
    /// Head
    size_t h;
    /// Tail
    size_t t;
    /// Elements
    sg_token_base* s;
    /// Next in chain
    struct __token_stack_link* nxt;
    /// Previous in chain
    struct __token_stack_link* prv;
} token_stack_link;

/// Can grow & be consumed in either direction
typedef struct {
    /// Front link
    token_stack_link* f;
    /// Back link
    token_stack_link* b;
} token_stack;

/** @brief Initialize the token stack
 */
void token_stack_init(token_stack* s);

/** @brief Release the token stack
 */
void token_stack_release(token_stack* s);

/** @brief Get token stack depth
 */
uint64 token_stack_depth(token_stack* s);

/** @brief Get token stack link depth
 */
uint64 token_link_depth(token_stack_link* l);

/** @brief Can we pop an element off the front of the link?
 */
int token_link_can_pop_front(token_stack_link* l);

/** @brief Can we pop an element off the back of the link?
 */
int token_link_can_pop_back(token_stack_link* l);

/** @brief Pop an element off the front of the link
 */
sg_token_base token_link_pop_front(token_stack_link* l);

/** @brief Pop an element off the back of the link
 */
sg_token_base token_link_pop_back(token_stack_link* l);

/** @brief Make a link with room at the end
 */
token_stack_link* make_token_link_e(size_t c);

/** @brief Make a link with room at the beginning
 */
token_stack_link* make_token_link_b(size_t c);

/** @brief Reclaim owned memory
 */
void release_token_link(token_stack_link* l);

/** @brief Push a token onto the front of the link
 */
int token_link_push_front(token_stack_link* l, sg_token_base t);

/** @brief Push a token onto the back of the link
 */
int token_link_push_back(token_stack_link* l, sg_token_base t);

/** @brief Push a token onto the front of the stack
 */
void token_stack_push_front(token_stack* s, sg_token_base t);

/** @brief Push a token onto the back of the stack
 */
void token_stack_push_back(token_stack* s, sg_token_base t);

/** @brief Can we pop a token from the front of the stack?
 */
int token_stack_can_pop_front(token_stack* s);

/** @brief Can we pop a token from the back of the stack?
 */
int token_stack_can_pop_back(token_stack* s);

/** @brief Pop a token from the front of the stack
 */
sg_token_base token_stack_pop_front(token_stack* s);

/** @brief Pop a token from the back of the stack
 */
sg_token_base token_stack_pop_back(token_stack* s);

/** @brief Is the token stack empty?
 */
int token_stack_empty(token_stack* s);

/** @brief Get the token on the front of the link without popping
 */
sg_token_base token_link_front(token_stack_link* l);

/** @brief Get the token on the front of the stack without popping
 */
sg_token_base token_stack_front(token_stack* s);

/** @brief Get the token on the back of the link without popping
 */
sg_token_base token_link_back(token_stack_link* l);

/** @brief Get the token on the back of the stack without popping
 */
sg_token_base token_stack_back(token_stack* s);

typedef struct {
    /// Parser state
    sm_c_parserState ps;
    /// Token stack
    token_stack* t;
    /// Token
    //sg_token_t tok;
    /// Load whitespace onto the stack
    //int load_stack;
    /// Stack to hold indentation state for off-side rule
    //indent_stack is;
    /** @brief Ghost token stack
     * @details See @ref GhostTokens "Ghost Tokens"
     */
    //ghost_stack gs;
    /// Just parsed an opening block?
    //int open_blk;
    
    //int start_blk;
    /** @brief Set to true to discard current token
     * @details If set, the scanner will automatically advance to
     * the next token when @ref scannerTok is called
     */
    int discard;
    /// Are we in a block controlled by indenting?
    //int indent_block;
    /// Tolerate off-side rule (identation-based grouping)
    int tol_offside;
    /// Tolerate brace-based grouping (delimited blocks)
    int tol_dlim_blocks;
    /// Indentation (tab set) level
    uint32 ind;
    /// Previous indentation (tab set) level
    uint32 iind;
    /// Line number
    uint64 ln;
    /// Suppress newline
    int suppress_newline;
} scanner_t;

//extern uint32 indent;

/** @brief Return a token without attributes
 */
sg_token_t makeToken(uint32 tval, uint64 ln);

/** @brief Initialize the scanner
 */
void initScanner(scanner_t* s, constLexBuf ubuf);

/** @brief Destructor for the scanner
 */
void finalizeScanner(scanner_t* s);

/** @brief Get the current token
 */
sg_token_t scannerTok(scanner_t* s);

/** @brief Determine if end of input
 */
int scannerEOI(scanner_t* s);

/** @brief Cause the scanner to emit a ghost token (see @ref GhostTokens "Ghost Tokens")
 */
void scannerEmit(scanner_t* s, uint32 tok);

/** @brief Advance the scanner
 */
void scannerAdvance(scanner_t* s);

void scannerDiscard(scanner_t* s);

/** @brief Convert buffer text to an unsigned int
 */
uint64 scannerIntFromTok(sg_token_t t);

void dumpToken(sg_token_t t);

typedef enum {
    BLOCK_DELIM_INVALID,
    BLOCK_DELIM_OFFSIDE,
    BLOCK_DELIM_PHRASE
} BLOCK_DELIM_TYPE;

typedef struct {
    /// Type: can be @ref BLOCK_DELIM_OFFSIDE, @ref BLOCK_DELIM_PHRASE
    uint32 t;
    /// Indentation level the block should close at
    uint32 tabbase;
    /// Indentation level for offside blocks
    uint32 tabset;
} block_info;

/** @brief Parse the opening of a block
 * @details This is necessary because of the various ways a block
 * can be started. The language must have predefined points at which
 * a block may be open to resolve the ambiguities that would
 * result otherwise
 */
block_info scannerOpenBlock(scanner_t* s);

/** @brief Parse the closing of a block
 * @param[in] b Info about the block; whether it is offside or phrase-based
 */
int scannerCloseBlock(scanner_t* s, block_info b);

/** @brief Returns true if we should close the block
 */
int scannerShouldCloseBlock(scanner_t* s, block_info b);

/** @brief Turn suppression of newline token on/off
 * @param state Desired suppression state (0 for no nl suppression)
 * @returns Previous state of newline supporession
 */
int scannerSuppNL(scanner_t* s, int state);

void scanString(scanner_t* s, sg_token_t* tok);


#ifdef __cplusplus
}//extern "C"
#endif

#endif
